using System;
using System.ComponentModel;
using System.Web;
using System.Web.Services;
using System.Xml;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Meetings;
using Microsoft.SharePoint.WebControls;

namespace Balticovo.SharePoint.WebServices
{
    /// <summary>
    /// Summary description for WebService1
    /// </summary>
    [WebService(Namespace = "http://schemas.microsoft.com/sharepoint/soap/")]
    //[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    //[System.Web.Script.Services.ScriptService]
    public class OutlookLists : System.Web.Services.WebService, IListsSoap
    {
        SPServiceLists.Lists _listsWebService;

        /// <summary>
        /// This webservice only gets invoked when outlook tryies to sync with lists.
        /// So if outlook tries to access lists.asmx, in global.asax we rewrite url to get to this web service
        /// In SharePoint you must modify "Connect to Outlook" button to pass in not list id, but view id.
        /// So for all methods outlook access listName is actually viewId.
        /// </summary>
        public OutlookLists()
        {
            //Web service that outlook was trying to access.
            _listsWebService = new SPServiceLists.Lists();
            //unwrites url (url that was requested by Outlook)
            _listsWebService.Url = Context.Request.Url.OriginalString.Replace(Context.Request.Url.PathAndQuery, Context.Request.RawUrl);
            _listsWebService.UseDefaultCredentials = true;
        }

        public System.Xml.XmlNode GetList(string listName)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            //It means that passed listName is not view ID.
            //Then probably javascript snippet that passes stssync url does not replace list ID with view ID.
            if (list == null)
                return _listsWebService.GetList(listName);
            else
                return _listsWebService.GetList(list.ID.ToString());

        }

        /// <summary>
        /// Outlook 2003 uses this method to get list items.
        /// </summary>
        /// <param name="listName"></param>
        /// <param name="viewName"></param>
        /// <param name="query"></param>
        /// <param name="viewFields"></param>
        /// <param name="rowLimit"></param>
        /// <param name="queryOptions"></param>
        /// <param name="webID"></param>
        /// <returns></returns>
        public System.Xml.XmlNode GetListItems(string listName, string viewName, System.Xml.XmlNode query, System.Xml.XmlNode viewFields, string rowLimit, System.Xml.XmlNode queryOptions, string webID)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.GetListItems(listName, viewName, query, viewFields, rowLimit, queryOptions, webID);
            else
            {
                //Be sure to query all items even if we are in multiple meeting workspace.
                int originalInstanceId = setMeetingsInstanceId(list.ParentWeb, (int)SPMeeting.SpecialInstance.AllWithSeries);
                try
                {
                    SPView view = list.Views[new Guid(listName)];
                    return _listsWebService.GetListItems(list.ID.ToString(), view.ID.ToString(), getQueryNode(view.Query), viewFields, rowLimit, queryOptions, webID);
                }
                finally
                {
                    setMeetingsInstanceId(list.ParentWeb, originalInstanceId);
                }
            }
        }

        /// <summary>
        /// Used by Outlook 2003 clients
        /// </summary>
        /// <param name="listName"></param>
        /// <param name="viewFields"></param>
        /// <param name="since"></param>
        /// <param name="contains"></param>
        /// <returns></returns>
        public System.Xml.XmlNode GetListItemChanges(string listName, System.Xml.XmlNode viewFields, string since, System.Xml.XmlNode contains)
        {              
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.GetListItemChanges(listName, viewFields, since, contains);
            else
            {
                int originalInstanceId = setMeetingsInstanceId(list.ParentWeb, (int)SPMeeting.SpecialInstance.AllWithSeries);
                try
                {
                    SPView view = list.Views[new Guid(listName)];
                    //TODO: Check if this actually works for outlook 2003 clients, because there is no QUERY parameter here.
                    return _listsWebService.GetListItemChanges(list.ID.ToString(), viewFields, since, contains);
                }
                finally
                {
                    setMeetingsInstanceId(list.ParentWeb, originalInstanceId);
                }
            }


        }

        /// <summary>
        /// Used by Outlook 2007 
        /// </summary>
        /// <param name="listName"></param>
        /// <param name="viewName"></param>
        /// <param name="query"></param>
        /// <param name="viewFields"></param>
        /// <param name="rowLimit"></param>
        /// <param name="queryOptions"></param>
        /// <param name="changeToken"></param>
        /// <param name="contains"></param>
        /// <returns></returns>
        public System.Xml.XmlNode GetListItemChangesSinceToken(string listName, string viewName, System.Xml.XmlNode query, System.Xml.XmlNode viewFields, string rowLimit, System.Xml.XmlNode queryOptions, string changeToken, System.Xml.XmlNode contains)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.GetListItemChangesSinceToken(listName, viewName, query, viewFields, rowLimit, queryOptions, changeToken, contains);
            else
            {
                int originalInstanceId = setMeetingsInstanceId(list.ParentWeb, (int)SPMeeting.SpecialInstance.AllWithSeries);
                try
                {
                    SPView view = list.Views[new Guid(listName)];
                    return _listsWebService.GetListItemChangesSinceToken(list.ID.ToString(), view.ID.ToString(), getQueryNode(view.Query), viewFields, rowLimit, queryOptions, changeToken, contains);
                }
                finally
                {
                    setMeetingsInstanceId(list.ParentWeb, originalInstanceId);
                }
            }
        }

        public System.Xml.XmlNode UpdateListItems(string listName, System.Xml.XmlNode updates)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.UpdateListItems(listName, updates);
            else
            {
                int originalInstanceId = setMeetingsInstanceId(list.ParentWeb, (int)SPMeeting.SpecialInstance.AllWithSeries);
                try
                {
                    return _listsWebService.UpdateListItems(list.ID.ToString(), updates);
                }
                finally
                {
                    setMeetingsInstanceId(list.ParentWeb, originalInstanceId);
                }
            } 
        }

        public System.Xml.XmlNode AddDiscussionBoardItem(string listName, byte[] message)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.AddDiscussionBoardItem(listName, message);
            else
                return _listsWebService.AddDiscussionBoardItem(list.ID.ToString(), message);
        }

        
        public string AddAttachment(string listName, string listItemID, string fileName, byte[] attachment)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.AddAttachment(listName, listItemID, fileName, attachment);
            else
                return _listsWebService.AddAttachment(list.ID.ToString(), listItemID, fileName, attachment);
        }

        public System.Xml.XmlNode GetAttachmentCollection(string listName, string listItemID)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                return _listsWebService.GetAttachmentCollection(listName, listItemID);
            else
                return _listsWebService.GetAttachmentCollection(list.ID.ToString(), listItemID);
        }

        public void DeleteAttachment(string listName, string listItemID, string url)
        {
            //Remember - listName is actually ID of view being synced with outlook.
            SPList list = getListFromView(listName);
            if (list == null)
                _listsWebService.DeleteAttachment(listName, listItemID, url);
            else
            {
                int originalInstanceId = setMeetingsInstanceId(list.ParentWeb, (int)SPMeeting.SpecialInstance.AllWithSeries);
                try
                {
                    _listsWebService.DeleteAttachment(list.ID.ToString(), listItemID, url);
                }
                finally
                {
                    setMeetingsInstanceId(list.ParentWeb, originalInstanceId);
                }
            }
                
        }


        /// <summary>
        /// Finds to which list view belongs.
        /// Throws SPException
        /// </summary>
        /// <param name="viewId"></param>
        /// <returns></returns>
        SPList getListFromView(string viewId)
        {
            SPWeb web = SPControl.GetContextWeb(HttpContext.Current);

            try
            {
                Guid viewGuid = new Guid(viewId);

                foreach (SPList list in web.Lists)
                {
                    foreach (SPView view in list.Views)
                    {
                        if (view.ID.Equals(viewGuid))
                            return list;
                    }
                }

                //List not yet found to this point. If there is no such List with passed ID, then this passed viewId was truly a view Id, but that view was probably deleted.
                bool isViewDeleted = true;
                foreach (SPList list in web.Lists)
                {
                    if (list.ID.Equals(viewGuid))
                        //Passed id is just a list (anyways there shouldn't be such ID passed to this webservice)
                        isViewDeleted = false;
                }

                if (isViewDeleted)
                    throw new SPException(string.Format("View {0} not found in {1} ", viewId, web.Url));
            }
            catch (ArgumentException ex) { return null; }



            return null;
        }

        /// <summary>
        /// Gets XmlNode from string
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        XmlNode getQueryNode(string query)
        {
            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml("<Query>" + query + "</Query>");
            return xDoc.SelectSingleNode("/Query");
        }

        /// <summary>
        /// Sets meeting instance id for multiple meeting workspace.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="newInstanceId"></param>
        /// <returns></returns>
        int setMeetingsInstanceId(SPWeb web, int newInstanceId)
        {
            SPMeeting meeting = SPMeeting.GetMeetingInformation(web);
            int previousInstanceId = meeting.InstanceId;
            meeting.InstanceId = newInstanceId;

            return previousInstanceId;
        }

        //Web service methods that are not used by Outlook.
        #region NotImplemented

        public System.Xml.XmlNode GetListAndView(string listName, string viewName)
        {
            throw new NotImplementedException();
        }

        public void DeleteList(string listName)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode AddList(string listName, string description, int templateID)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode AddListFromFeature(string listName, string description, Guid featureID, int templateID)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode UpdateList(string listName, System.Xml.XmlNode listProperties, System.Xml.XmlNode newFields, System.Xml.XmlNode updateFields, System.Xml.XmlNode deleteFields, string listVersion)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode GetListCollection()
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode GetVersionCollection(string strlistID, string strlistItemID, string strFieldName)
        {
            throw new NotImplementedException();
        }


        public bool CheckOutFile(string pageUrl, string checkoutToLocal, string lastmodified)
        {
            throw new NotImplementedException();
        }

        public bool UndoCheckOut(string pageUrl)
        {
            throw new NotImplementedException();
        }

        public bool CheckInFile(string pageUrl, string comment, string CheckinType)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode GetListContentTypes(string listName, string contentTypeId)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode GetListContentType(string listName, string contentTypeId)
        {
            throw new NotImplementedException();
        }

        public string CreateContentType(string listName, string displayName, string parentType, System.Xml.XmlNode fields, System.Xml.XmlNode contentTypeProperties, string addToView)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode UpdateContentType(string listName, string contentTypeId, System.Xml.XmlNode contentTypeProperties, System.Xml.XmlNode newFields, System.Xml.XmlNode updateFields, System.Xml.XmlNode deleteFields, string addToView)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode DeleteContentType(string listName, string contentTypeId)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode UpdateContentTypeXmlDocument(string listName, string contentTypeId, System.Xml.XmlNode newDocument)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode UpdateContentTypesXmlDocument(string listName, System.Xml.XmlNode newDocument)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode DeleteContentTypeXmlDocument(string listName, string contentTypeId, string documentUri)
        {
            throw new NotImplementedException();
        }

        public System.Xml.XmlNode ApplyContentTypeToList(string webUrl, string contentTypeId, string listName)
        {
            throw new NotImplementedException();
        }

#endregion

        [WebMethod()]
        public string ATestMethod()
        {

            //ProviderManager pm = new ProviderManager();
            //IProvider prov = pm.GetProvider(new Guid("{7765B84F-6D32-4d31-B28E-6BC615D2F187}"));
            //ChangeKey ck = new ChangeKey("1; 1; 0ac863f6-3a07-4d5e-bd07-78bebb0a34f1; 633033246547900000; 217 ");
            //ChangeKey ck = new ChangeKey(1, 1, Guid.NewGuid(), DateTime.Now, 23); 
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            //sb.AppendLine("ChangeEventType = " + ck.ChangeEventType.ToString());
            //sb.AppendLine("ChangeNumber = " + ck.ChangeNumber.ToString());
            //sb.AppendLine("ChangeObjectType = " + ck.ChangeObjectType.ToString());
            //sb.AppendLine("ChangeTime = " + ck.ChangeTime.ToString());
            //sb.AppendLine("ObjectGuid = " + ck.ObjectGuid.ToString());
            //sb.AppendLine("ChangeKey = " + ck.ToString());

            return null;
        }

    }
}
